/*
 * Created on 2005-7-16
 *
 */
package bma.common.langutil.bean.impl;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import bma.common.langutil.bean.BeanClass;
import bma.common.langutil.bean.BeanObjectProperty;
import bma.common.langutil.bean.common.ClassDialect;
import bma.common.langutil.bean.common.CommonClassDialect;
import bma.common.langutil.bean.common.PropertyMethodInfo;
import bma.common.langutil.core.ClassUtil;
import bma.common.langutil.core.MType;
import bma.common.langutil.core.ObjectUtil;

/**
 * 构造BeanClass
 * 
 * @author guanzhong
 * @since 1.0 (imported from BMA.ESP Project)
 */
public class BeanClassBuilderNative {

	final org.slf4j.Logger log = org.slf4j.LoggerFactory
			.getLogger(BeanClassBuilderNative.class);

	private ClassDialect dialect = null;

	public BeanClassBuilderNative() {
		super();
	}

	public ClassDialect getDialect() {
		return dialect;
	}

	public void setDialect(ClassDialect dialect) {
		this.dialect = dialect;
	}

	public static BeanClass createNativeClass(Class cls) {
		if (ClassUtil.isImplementInterface(cls, Collection.class)) {
			return new BeanListNative(cls);
		}
		if (ClassUtil.isImplementInterface(cls, Map.class)) {
			return new BeanMapNative(cls);
		}
		return null;
	}

	public BeanClass createClass(Class cls) throws Exception {

		BeanClass r = null;
		r = createNativeClass(cls);
		if (r != null)
			return r;

		ClassDialect dialect = getDialect();
		if (dialect == null)
			dialect = CommonClassDialect.getInstance();

		BeanObjectSimple bean = new BeanObjectSimple(cls);

		Map<String, BeanObjectPropertyNative> propMap = new HashMap<String, BeanObjectPropertyNative>();
		Method[] ms = cls.getMethods();

		for (Method m : ms) {
			if (Modifier.isStatic(m.getModifiers()))
				continue;

			PropertyMethodInfo mtype = dialect.properyMethod(m);
			if (mtype == null)
				continue;

			MType propType = null;
			if (mtype.isSetMethod()) {
				// set
				Type[] param = m.getGenericParameterTypes();
				if (param == null)
					continue;
				if (param.length != 1)
					continue;
				propType = new MType(param[0]);
			} else {
				// get
				Type rt = m.getGenericReturnType();
				if (rt == Void.class)
					continue;
				propType = new MType(rt);
			}

			String propName = mtype.getName();
			BeanObjectPropertyNative prop = propMap.get(propName);
			if (prop == null) {
				prop = new BeanObjectPropertyNative();
				prop.setName(propName);
				prop.setType(propType);
				prop.setPrototype(m);
				if (log.isDebugEnabled()) {
					log.debug("{} add property => {}:{}",
							new Object[] { cls.getName(), propName, propType });
				}
				propMap.put(propName, prop);
			} else {
				// check type
				if (!ObjectUtil.equals(prop.getType().getType(),
						propType.getType())) {
					if (log.isWarnEnabled()) {
						log.warn("{} property({}) conflict {}/{}",
								new Object[] { cls.getName(), propName,
										propType, prop.getType() });
					}
					continue;
				}
			}

			if (mtype.isSetMethod()) {
				// set
				if (prop.getPrototype() == null) {
					prop.setPrototype(m);
				}
				prop.setSetMethod(m);
			} else {
				// get
				prop.setPrototype(m);
				prop.setGetMethod(m);
			}
		}
		bean.setProperties(new ArrayList<BeanObjectProperty>(propMap.values()));
		return bean;

	}
}
